#include "file.h"

#ifdef _MSC_VER
#include <windows.h>
#endif

namespace util {

File::File()
{

}

File::~File()
{
    close();
}

void File::setFileName(const std::string &fileName)
{
    m_fileName = fileName;
    m_fs.clear();
}

bool File::open(OpenMode mode)
{
    close();

    if(mode == NotOpen)
        mode = (OpenMode)(ReadOnly | Text);

    std::ios_base::openmode _Mode = std::ios_base::in | std::ios_base::binary;
    if(mode & ReadOnly)
        _Mode |= std::ios_base::in;
    if(mode & WriteOnly)
        _Mode |= std::ios_base::out | std::ios_base::trunc;
    if(mode & Append)
        _Mode |= std::ios_base::ate;
    if(mode & Truncate)
        _Mode |= std::ios_base::trunc;
    if(mode & Text)
        _Mode &= ~std::ios_base::binary;
#ifdef _MSC_VER
    auto stringToWstring = [](const std::string &str) -> std::wstring
    {
        if(str.empty()){
            return std::wstring();
        }
        int wchars_num = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
        std::wstring wstr(wchars_num, 0);
        MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &wstr[0], wchars_num);
        return wstr;
    };
    m_fs.open(stringToWstring(m_fileName), _Mode);
#else
    m_fs.open(m_fileName, _Mode);
#endif
    m_mode = mode;
    return m_fs.is_open();
}

bool File::isOpen() const
{
    return m_fs.is_open();
}

void File::close()
{
    if(m_fs.is_open())
        m_fs.close();
    m_fs.clear();
}

void File::flush()
{
    m_fs.flush();
}

bool File::eof() const
{
    return m_fs.eof();
}

bool File::seek(long long pos)
{
    if(!isOpen())
        return false;
    std::ios_base::seekdir _dir = std::ios_base::beg;
    if(pos < 0) {
        pos = 0;
        _dir = std::ios_base::end;
    }

    if(m_mode & ReadOnly)
        m_fs.seekg(pos, _dir);
    else
        m_fs.seekp(pos, _dir);
    return !m_fs.fail();
}

long long File::pos()
{
    long long pos = -1;
    if(!isOpen())
        return 0;
    if(m_mode & ReadOnly)
        pos = m_fs.tellg();
    else
        pos = m_fs.tellp();
    return pos;
}

long long File::fileSize()
{
    if(!isOpen())
        return 0;
    long long file_size = 0;
    auto curr = pos();
    if(seek(-1)) {
        file_size = pos();
        if(!seek(curr)) {
            file_size = 0;
        }
    }

    if(file_size < 0)
        file_size = 0;
    return file_size;
}

size_t File::read(char *buf, size_t size)
{
    if(!isOpen())
        return 0;
    m_fs.read(buf, size);
    if(m_fs.gcount() > 0) {
        return m_fs.gcount();
    }
    return 0;
}

size_t File::write(const char *buf, size_t size)
{
    if(!isOpen())
        return 0;
    m_fs.write(buf, size);
    if(!m_fs.fail()) {
        return size;
    }
    return 0;
}

}
